package cn.com.chengmandian.core.web.filters;

import cn.com.chengmandian.core.common.constants.CommonConstant;
import cn.com.chengmandian.core.common.constants.ResponseCode;
import cn.com.chengmandian.core.common.utils.UrlUtil;
import cn.com.chengmandian.core.redis.cache.AppCache;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.pojo.AppInfo;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;
import java.util.TreeMap;

@Slf4j
@Component
public class OuterAuthFilter extends OncePerRequestFilter {
    private final static String outerUrl = "/*/outer/**";

    private final static String SIGN = "sign";

    private final static String APP_KEY = "appKey";

    private final static String TIMESTAMP = "timestamp";

    @Autowired
    private AppCache appCache;

    // 403 权限不足
    private void responseError(HttpServletResponse response, Integer code, String message) {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONUtil.toJsonStr(AjaxResult.error(code, message)));
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String requestURI = request.getRequestURI();

        if (!UrlUtil.filterUrls(outerUrl, requestURI)) {
            filterChain.doFilter(request, response);
            return;
        }
        String sign = request.getParameter(SIGN);
        if (StrUtil.isBlank(sign)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "缺少sign参数");
            return;
        }
        String appKey = request.getParameter(APP_KEY);
        if (StrUtil.isBlank(appKey)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "缺少appKey参数");
            return;
        }
        String timestamp = request.getParameter(TIMESTAMP);
        if (StrUtil.isBlank(timestamp)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "缺少timestamp参数");
            return;
        }
        try {
            if (DateUtil.between(new Date(), new Date(new Long(timestamp) * 1000), DateUnit.SECOND, true) > 60 * 3) {
                responseError(response, ResponseCode.ACCESS_DENIED, "请求时间与服务器时间差异较大");
                return;
            }
        } catch (Exception e) {
            responseError(response, ResponseCode.ACCESS_DENIED, e.getMessage());
            return;
        }
        AppInfo appInfo = appCache.get(appKey);
        if (appInfo == null) {
            responseError(response, ResponseCode.ACCESS_DENIED, "不存在的appKey");
            return;
        }
        if (appInfo.getIsEnabled().equals(CommonConstant.ZERO)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "应用不可用");
            return;
        }

        Enumeration<String> names = request.getParameterNames();
        Map<String, String> params = new TreeMap<>();
        while (names.hasMoreElements()) {
            String key = names.nextElement();
            if (!key.equals(SIGN)) {
                params.put(key, request.getParameter(key));
            }
        }
        String signUrl = requestURI + "?" +  MapUtil.join(params, "&", "=");
        String signLocal = SecureUtil.md5(signUrl + appInfo.getAppSecret());
        if (!sign.equals(signLocal)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "签名错误");
            return;
        }
        filterChain.doFilter(request, response);
    }
}
